﻿using System;
using System.Threading;
using Yto.Common;
using Yto.Common.Dependency;

namespace Yto.Threading
{
    public class BaseTimer : BaseRunnable, ITransient
    {
        private readonly Timer _thrdTimer;
        private volatile bool _running;
        private volatile bool _runningThrd;

        public int Period { get; set; }

        public bool IsRunAtStart { get; set; }
        public EventHandler Elasped;

        public BaseTimer()
        {
            _thrdTimer = new Timer(callback, null, Timeout.Infinite, Timeout.Infinite);
        }

        public BaseTimer(int period, bool isRunAtStart = false) 
            : this()
        {
            Period = period;
            IsRunAtStart = IsRunAtStart;
        }

        private void callback(object state)
        {
            lock (_thrdTimer)
            {
                if(!_running || _runningThrd)
                {
                    return;
                }
                _thrdTimer.Change(IsRunAtStart ? 0 : Period, Timeout.Infinite);
                _runningThrd = true;
            }

            try
            {
                if (Elasped != null)
                {
                    Elasped(this, new EventArgs());
                }
            }
            finally
            {
                lock (_thrdTimer)
                {
                    _runningThrd = false;
                    if (_running)
                    {
                        _thrdTimer.Change(Period, Timeout.Infinite);
                    }
                    Monitor.Pulse(_thrdTimer);
                }
            }
        }

        public override void Start()
        {
            if (Period <= 0)
            {
                throw new BaseException("Period is not current!");
            }
            base.Start();

            _running = true;
            _thrdTimer.Change(IsRunAtStart ? 0 : Period, Timeout.Infinite);
        }

        public override void Stop()
        {
            lock (_thrdTimer)
            {
                _running = false;
                _thrdTimer.Change(Timeout.Infinite, Timeout.Infinite);
            }
            base.Stop();
        }

        public override void Wait2Stop()
        {
            lock (_thrdTimer)
            {
                while (_runningThrd)
                {
                    Monitor.Wait(_thrdTimer);
                }
            }
            base.Wait2Stop();
        }
    }
}
